﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace System.Erp.Runtime.Composition
{
    /// <summary>
    /// 提供一种IPartCatalog的基础实现，用于描述零件的组合，可以称为功能包。
    /// </summary>
    /// <remarks>
    /// <para>必须重载此类型才能使用，在派生类中实现其<see cref="CreateCatalogs"/>方法和<see cref="CreateParts"/>方法</para>
    /// </remarks>
    [Serializable]
    public abstract class PartCatalogBase : IPartCatalog
    {
        /// <summary>
        /// 创建 PartCatalogBase实例
        /// </summary>
        protected PartCatalogBase()
        {
        }

        #region 属性及部分接口实现

        /// <summary>
        /// 返回此组合下的所有子组合
        /// </summary>
        /// <returns>返回所有可用的零件组合对象的只读集合，每个组合对应一个动作，用于描述是应用还是取消</returns>
        ReadOnlyCollection<Tuple<PartAction, IPartCatalog>> IPartCatalog.Catalogs
        {
            get
            {
                if (_catalogs == null)
                {
                    lock (this)
                    {
                        if (_catalogs == null)
                        {
                            _catalogs = this.CreateCatalogs();
                        }
                    }
                }
                return _catalogs;
            }
        }

        [NonSerialized]
        private ReadOnlyCollection<Tuple<PartAction, IPartCatalog>> _catalogs;

        /// <summary>
        /// 空的组合
        /// </summary>
        protected readonly static ReadOnlyCollection<Tuple<PartAction, IPartCatalog>> EmptyCatalogs =
            new ReadOnlyCollection<Tuple<PartAction,IPartCatalog>>(new Tuple<PartAction,IPartCatalog>[0]);

        /// <summary>
        /// 派生类需要重载此方法构建<see cref="IPartCatalog.Catalogs"/>集合。
        /// </summary>
        protected abstract ReadOnlyCollection<Tuple<PartAction, IPartCatalog>> CreateCatalogs();

        /// <summary>
        /// 返回包中的直接零件集合
        /// </summary>
        /// <returns>所有可用的零件集合的查询器.</returns>
        ReadOnlyCollection<Tuple<PartAction, IPart>> IPartCatalog.Parts
        {
            get
            {
                if (_parts == null)
                {
                    lock (this)
                    {
                        if (_parts == null)
                        {
                            _parts = this.CreateParts();
                        }
                    }
                }
                return _parts;
            }
        }

        [NonSerialized]
        private ReadOnlyCollection<Tuple<PartAction, IPart>> _parts;
        /// <summary>
        /// 派生类需要重载此方法构建<see cref="IPartCatalog.Parts" />集合。
        /// </summary>
        protected abstract ReadOnlyCollection<Tuple<PartAction, IPart>> CreateParts();

        /// <summary>
        /// 空的零件
        /// </summary>
        protected readonly static ReadOnlyCollection<Tuple<PartAction, IPart>> EmptyParts =
            new ReadOnlyCollection<Tuple<PartAction, IPart>>(new Tuple<PartAction, IPart>[0]);

        #endregion

        #region 计算结果集

        [NonSerialized]
        private IEnumerable<IPart> _resultCache;

        /// <summary>
        /// 返回此组合最终的结果集。
        /// </summary>
        /// <returns>也用的结果集的查询对象。</returns>
        public IEnumerable<IPart> GetResult()
        {
            if (_resultCache == null)
            {
                lock (this)
                {
                    if (_resultCache == null)
                    {
                        HashSet<IPart> result = new HashSet<IPart>(PartEqualityComparer.Default);
                        GetResultCore(result);
                        _resultCache = result.ToArray();
                    }
                }
            }
            return _resultCache;
        }

        /// <summary>
        /// 创建缓存的结果集
        /// </summary>
        /// <returns>新的结果集</returns>
        protected virtual void GetResultCore(HashSet<IPart> result)
        {
            IEnumerable<IPart> itemResult;
            //TODO: 需要一个上下文对象，检测是否发生死循环。
            //这里没有调用Catalogs属性，是减少运行时不必要的内存占用，运算完后就不需要记录了。
            foreach (var catalogItem in this.CreateCatalogs())
            {
                if (catalogItem.Item1 == PartAction.Apply)
                {
                    itemResult = catalogItem.Item2.GetResult();
                    result.UnionWith(itemResult);
                }
                else
                {
                    itemResult = catalogItem.Item2.GetResult();
                    result.ExceptWith(itemResult);
                }
            }

            foreach (var partItem in this.CreateParts())
            {
                if (partItem.Item1 == PartAction.Apply)
                {
                    result.Add(partItem.Item2);
                }
                else
                {
                    result.Remove(partItem.Item2);
                }
            }
        } 
        #endregion

    }
}